#!/usr/bin/env python


#Import Modoules
import os,sys,math,traceback
import pygame, pygame.font, pygame.image, pygame.mixer, pygame.transform,pygame.sprite,pygame.constants
from pygame.locals import *
from ini import ini_file
from common import *
#from menu import *
from gamemap import *
from gui import *
from maploader import *
from settingsbuilder import SettingsBuilder

#----------------------------------------------------------

#-----------------------------------------------------------

class MapCursor:
    def __init__(self,map,max_tiles):
        self.startingPos = [0,0]
        self.pos = self.startingPos
        self.map = map
        self.tile_w = map.tile_w
        self.tile_h = map.tile_h
        self.max_tiles = max_tiles
        self.tilenr = 1
        self.changed = 1
        self.controls = {K_SPACE:self.select,
                         K_DOWN:self.down,
                         K_LEFT:self.left,
                         K_RIGHT:self.right,
                         K_UP:self.up,
                         K_PAGEUP:self.prev,
                         K_PAGEDOWN:self.next,
                         K_DELETE:self.clear
                         }
        
    def updateKeyPress(self,key_state,dt):
        ''' update the player acording to key_state '''
        #loop the control bindings and apply the function with arguments if the key matches
        for key in self.controls.keys(): 
            if key_state[key]:
                apply(self.controls[key]) # add dt to args
                self.changed = 1
                tile_pos =  self.map.getMapXYAtPos(self.pos[0],self.pos[1])

    def keepBounds(self):
        if self.tilenr < 1:                    self.tilenr = self.max_tiles-2
        if self.tilenr > self.max_tiles-2:     self.tilenr = 1

    def select(self):
        tile_pos =  self.map.getMapXYAtPos(self.pos[0],self.pos[1])
        self.map.setTileType(tile_pos[0],tile_pos[1],self.tilenr)
    def clear(self):
        tile_pos =  self.map.getMapXYAtPos(self.pos[0],self.pos[1])
        self.map.setTileType(tile_pos[0],tile_pos[1],0)

    def left(self):
        self.pos[0] -= self.tile_w
        if self.pos[0]< 0:    self.pos[0] = 0

    def right(self):
        self.pos[0] += self.tile_w

    def up(self):
        self.pos[1] -= self.tile_h
        if self.pos[1]< 0:    self.pos[1] = 0

    def down(self):
        self.pos[1] += self.tile_h

    def prev(self):
        self.tilenr -= 1
        self.keepBounds()

    def next(self):
        self.tilenr += 1
        self.keepBounds()
    
    def getPos(self):
        return self.pos
    def getMapPos(self):
        return self.map.getMapXYAtPos(self.pos[0],self.pos[1])

    def draw(self,screen,viewport):
        rect = (self.pos[0]-viewport[0]+viewport.screen_pos[0],self.pos[1]-viewport[1]+viewport.screen_pos[1],self.tile_w,self.tile_h)
        pygame.draw.rect(screen,(255,255,255),rect,1)


class MapEditor:
    class Error(Exception):
        pass
    # Pallet for map bmp's
    map_pal = ((155, 155, 155), (0, 0, 0), (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 240), (255, 160, 0), (214, 24, 206), (111, 19, 153), (24, 117, 109), (160, 160, 0), (0, 0, 0), (4, 0, 0), (59, 0, 86), (60, 191, 247), (132, 0, 0), (0, 0, 0), (42, 0, 0), (143, 0, 85), (103, 132, 52), (24, 0, 0), (38, 0, 86), (1, 191, 247), (229, 0, 0), (236, 1, 2), (184, 191, 249), (84, 191, 247), (152, 0, 0), (109, 0, 86), (143, 191, 247), (0, 132, 52), (52, 0, 0), (0, 62, 143), (70, 0, 0), (146, 0, 0), (170, 0, 2), (175, 0, 0), (55, 0, 0), (0, 1, 79), (55, 62, 143), (152, 1, 79), (207, 61, 118), (0, 0, 4), (0, 0, 0), (2, 147, 103), (85, 0, 42), (0, 0, 0), (0, 0, 132), (16, 61, 118), (1, 0, 0), (132, 0, 0), (255, 122, 100), (152, 62, 143), (207, 61, 189), (143, 255, 255), (0, 0, 1), (0, 62, 143), (218, 157, 148), (3, 0, 0), (2, 147, 103), (167, 158, 157), (42, 0, 0), (0, 0, 85), (60, 0, 132), (0, 0, 0), (2, 157, 148), (249, 22, 175), (0, 23, 143), (85, 0, 42), (132, 0, 0), (2, 157, 148), (2, 157, 148), (0, 0, 0), (211, 132, 220), (189, 132, 250), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 157, 148), (2, 156, 4), (0, 0, 0), (167, 41, 228), (189, 133, 26), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 156, 4), (2, 155, 44), (0, 0, 0), (232, 23, 167), (189, 133, 58), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 155, 44), (2, 152, 192), (0, 0, 0), (0, 0, 0), (189, 133, 90), (1, 23, 143), (143, 133, 208), (85, 0, 42), (2, 152, 192), (20, 0, 0), (82, 133, 228), (1, 23, 167), (246, 0, 0), (13, 0, 100), (0, 0, 22), (60, 2, 0), (143, 134, 40), (26, 32, 74), (0, 0, 2), (42, 2, 0), (0, 0, 85), (100, 88, 246), (85, 0, 42), (0, 0, 0), (0, 0, 0), (0, 0, 0), (3, 49, 159), (0, 0, 0), (0, 0, 0), (159, 49, 159), (1, 0, 0), (190, 22, 183), (0, 3, 116), (0, 0, 0), (3, 0, 0), (0, 1, 17), (167, 0, 0), (13, 16, 227), (42, 0, 22), (13, 0, 85), (0, 0, 22), (1, 2, 0), (0, 0, 0), (159, 0, 0), (2, 157, 148), (159, 0, 0), (8, 6, 60), (0, 11, 124), (0, 0, 2), (0, 0, 1), (0, 2, 0), (40, 0, 0), (159, 62, 143), (88, 0, 0), (0, 22, 175), (84, 49, 159), (135, 158, 126), (1, 0, 0), (0, 0, 2), (0, 2, 0), (0, 0, 0), (143, 134, 40), (136, 152, 192), (60, 0, 88), (0, 2, 0), (22, 0, 13), (100, 88, 246), (85, 0, 42), (140, 0, 0), (88, 0, 2), (78, 30, 136), (1, 169, 141), (136, 30, 88), (92, 134, 92), (0, 170, 60), (0, 152, 192), (74, 49, 159), (159, 73, 126), (18, 0, 1), (12, 0, 0), (136, 0, 88), (0, 0, 32), (88, 0, 12), (251, 0, 0), (0, 108, 55), (57, 134, 172), (2, 155, 44), (2, 152, 192), (240, 22, 175), (12, 0, 0), (186, 0, 88), (36, 108, 55), (32, 0, 0), (48, 0, 0), (1, 0, 0), (192, 93, 225), (194, 0, 2), (167, 3, 219), (0, 0, 0), (36, 2, 0), (192, 0, 32), (207, 0, 2), (191, 135, 220), (87, 5, 151), (207, 0, 0), (103, 252, 122), (12, 6, 12), (36, 0, 32), (0, 0, 0), (0, 2, 0), (2, 0, 0), (0, 194, 204), (140, 0, 6), (217, 22, 207), (95, 191, 247), (0, 0, 0), (103, 0, 86), (71, 0, 0), (240, 191, 247), (0, 49, 216), (0, 49, 200), (223, 135, 28), (226, 1, 63), (1, 49, 200), (94, 1, 87), (63, 74, 233), (87, 19, 100), (0, 49, 216), (0, 49, 200), (86, 135, 94), (86, 135, 70), (0, 0, 0), (186, 0, 1), (0, 0, 1), (0, 49, 200), (87, 3, 7), (0, 0, 215), (94, 126, 160), (0, 135, 92), (152, 0, 12), (208, 0, 86), (52, 0, 14), (0, 0, 0), (145, 0, 89), (0, 191, 247), (52, 0, 88), (90, 9, 170), (0, 151, 162), (146, 0, 0), (170, 100, 58), (38, 2, 38), (223, 79, 77), (106, 0, 1), (207, 0, 0), (72, 135, 168), (170, 4, 95), (134, 0, 0), (162, 139, 202), (162, 151, 162), (24, 112, 0), (255, 0, 0), (255, 255, 255))

    def __init__(self,map_creator,log=sys.stderr):
        self.settings = None
        self.delay = 5
        self.log = log
        self.map_creator = map_creator
        self.clock = pygame.time.Clock()
        self.running = 0
        
    def initDisplay(self,settings):
        #-------------------------------------
        # Setup display
        SCREEN_W,SCREEN_H = settings['graphics']['res']
        ' calculates the flags to give to pygame diaplay '
        if settings['graphics']['doublebuff']:
            settings['graphics']['flags'] |= DOUBLEBUF
        else:
            settings['graphics']['flags'] &= ~DOUBLEBUF
        if settings['graphics']['fullscreen']:
            settings['graphics']['flags'] |= FULLSCREEN
        else:
            settings['graphics']['flags'] &= ~FULLSCREEN
        if     settings['graphics']['hardware']:
            settings['graphics']['flags'] |= HWSURFACE
        else:
            settings['graphics']['flags'] &= ~HWSURFACE
                
        flags = settings['graphics']['flags']
        bpp = settings['graphics']['bpp']

        # Init PyGame Display
        if bpp:    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H), flags,bpp)
        else:    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H), flags)
        pygame.display.set_caption('Turb')
        pygame.mouse.set_visible(0)

        # Set pyUI screen to new screen
        r = pyui.desktop.getRenderer()
        r.screen = screen

        # Show display info
        if screen.get_flags() & HWSURFACE == HWSURFACE:
            print 'Hardware Accelerated'
        if screen.get_flags() & DOUBLEBUF == DOUBLEBUF:
            print 'Double Buffered'

        return screen
    
    def initMap(self,map_name):
        ' creates the map object and returns it '
        self.log.write('loading Map \"%s\"\n'%map_name)
        game_map = self.map_creator.getGameMap(map_name)
        return game_map

    def start(self,settings):
        ' implement this in derived classes '
        self.settings = settings

        self.game_map = self.initMap(settings['game']['map'])
        self.tile_w,self.tile_h = self.game_map.tile_w,self.game_map.tile_h

        # create a map to show all tiles in
        max_tiles = int(self.game_map.tiles.get_height() / self.tile_w)+2
        from array import array
        tiles_map = Matrix()
        for i in range(1,max_tiles):
            tiles_map.append([i,])
        self.tile_map = GameMap(tiles_map,self.tile_w,
                                self.tile_h,self.game_map.tiles)

        self.cursor = MapCursor(self.game_map, max_tiles)
        self.screen = self.initDisplay(settings)

        self._run()

    def _run(self):
        ''' Runs the gameengine when every thing is initialyzed'''
        settings = self.settings
        screen = self.screen

        tile_w,tile_h = self.tile_w,self.tile_h
        # setup Viewports
        h = screen.get_height()
        w = screen.get_width()
        map_viewport = ViewPort(0,0,w,h-tile_h*2,screen_pos=(0,0),screen=screen)
        tiles_viewport = ViewPort(0,h,w,tile_h*2,screen_pos=(0,h-tile_h*2),screen=screen)

        #-------------------------------------
        # Setup game graphics
        forground_layer = self.map_creator.getTiledLayer(0)
        tiles_layer = TiledLayer(self.tile_map)

        max_fps = settings['graphics']['max_fps']
        font = pygame.font.Font(None, 18)

        black = (0,0,0)
        clock = self.clock
        clock.tick()

        cursor = self.cursor

        delay = self.delay # time to sleep between every frame
        self.running = 1
        pygame.key.set_repeat(400,30) 
        keystate = pygame.key.get_pressed()

        #------------------- Main Game loop -----------------
        while (self.running):
            # calculate time since last update 
            dt = float(clock.tick(max_fps))/1000

            # wait for input
            event = pygame.event.wait()
            if event.type == KEYDOWN:
                keystate = pygame.key.get_pressed()
                # Check if quit
                if self.checkQuit(keystate,screen,clock,dt):
                    self.quit()
                cursor.updateKeyPress(keystate,dt)
                
            # let operating system breath 
            pygame.time.delay(delay)

            # Clear screen 
            screen.fill(black)

            # ------------- Draw game on screen ------------------
            # Update viewport position 
            (map_viewport.centerx, map_viewport.centery) = self.cursor.getPos()

            # Render layers 
            if cursor.changed:
                forground_layer.forceRecalc()
                cursor.changed = 0
            forground_layer.render(map_viewport)
            cursor.draw(screen,map_viewport)

            viewport = tiles_viewport

            (tiles_viewport.centerx, tiles_viewport.centery) = (cursor.tilenr*tile_w,0)
            tiles_layer.render(tiles_viewport)

            pos = ((cursor.tilenr)*tile_w-tile_w,0)
            rect = (pos[0]-viewport[0]+viewport.screen_pos[0],pos[1]-viewport[1]+viewport.screen_pos[1],tile_w,tile_h)
            pygame.draw.rect(screen,(255,255,155),rect,1)

            self.drawMap(map_viewport,cursor.getMapPos())

            # get info and print draw in on screen
            tp = cursor.getMapPos()
            tile_nr = self.game_map.getTileType(tp[0],tp[1])
            p =(map_viewport.centerx, map_viewport.centery)
            info_string = r"Tile nr:%s New Tile nr:%d Tile pos:%dx%d Pos:%dx%d"%(tile_nr,cursor.tilenr,tp[0],tp[1],p[0],p[1])
            map_viewport.blit(font.render(info_string, 1, (255,255,255)),(0,map_viewport.height-20))

            # make changes visible 
            pygame.display.flip()

    def drawMap(self,viewport,cursorpos):
        ' draws an overview of the map '
        map_ = self.game_map.map
        map_string = r''.join([chr(i) for i in map_.getFlatList()])
        new_map = pygame.image.fromstring(map_string, map_.shape, 'P')
##        new_map = pygame.surfarray.make_surface(self.game_map.map)            
        new_map.set_palette(self.map_pal)
        try:        
            new_map.set_at(cursorpos,255)
        except IndexError,errmsg:
            self.log.write("%s\n"%errmsg)
        r = new_map.get_rect()
        # zoom the overview bitmap
        zfactor = 3
        new_map = pygame.transform.scale(new_map,(r[2]*zfactor,r[3]*zfactor))
        new_map = new_map.convert()
        new_map.set_alpha(198)

        viewport.blit(new_map,(0,0))


    def save_map(self):
        ' Convert the map to image and save it '
        outfile = "new_map.bmp"
        map_ = self.game_map.map
        map_string = r''.join([chr(i) for i in map_.getFlatList()])
        new_map = pygame.image.fromstring(map_string, map_.shape, 'P')
##        new_map = pygame.surfarray.make_surface(self.game_map.map)            
        new_map.set_palette(self.map_pal)
        import os
        outfile = os.path.abspath(outfile)
        self.log.write("Saving file as %s\n"%outfile)
        self.log.write("Map Dimensions %dx%d\n"%(new_map.get_width(),new_map.get_height()))
        new_map.set_palette(self.map_pal)
        # save the map image
        pygame.image.save(new_map, outfile) 

    def checkQuit(self,keystate,screen,clock,dt):
        ''' Check if quit event happend during play and asks Y/N'''
        if keystate[K_ESCAPE] or pygame.event.peek(QUIT):
            keystate = pygame.key.get_pressed()
            quitfont = pygame.font.Font(None, 30)
            info_string = "Quit? y/n or s to save"
            ask_surf=quitfont.render(info_string, 1, (255,200,100),(50,50,70))
            h = screen.get_height()
            w = screen.get_width()
            screen.blit(ask_surf,(w/2-ask_surf.get_width()/2,h/2-ask_surf.get_height()/2))
            # make changes visible 
            pygame.display.flip()
            while 1:
                event = pygame.event.wait()
                if event.type == QUIT or (event.type == KEYDOWN and event.key == K_y):
                    #net_controller.close() # close net connection
                    return 1
                elif event.type == KEYDOWN and event.key == K_n:
                    # reset delta time before continuing
                    clock.tick()
                    dt = 0
                    return 0
                elif event.type == KEYDOWN and event.key == K_s:
                    # reset delta time before continuing
                    self.save_map()
                    clock.tick()
                    dt = 0
                    return 0


    def quit(self,save=0):
        self.log.write("Shutting down GameEngine\n")
        if save: self.save_map()

        self.running = 0

def main():
    log = sys.stderr
    map_creator = MapLoader(r'./Maps')

    log.write("Parsing Settings ini file and command line\n")
    settings = SettingsBuilder(map_creator).getSettings()
    log.write("Starting pyUI\n")
    global gui
    gui = GUI(settings)
    settings =     gui.run(fullscreen = settings['graphics']['fullscreen'])
    editor = MapEditor(map_creator)

    while 1:
        if not pygame.display.get_init(): break
        pygame.mouse.set_visible(0)

        log.write("Starting Game\n")
        editor.start(settings)
        keystate = pygame.key.get_pressed() 

##        try:
##            game.startGame(settings)
##        except Exception,errmsg:
##            self.log.write('%s\n'%errmsg)

        pygame.mouse.set_visible(1)
        gui.run()
    editor.quit()
#----------------------------------------------------------
#    EOF
#----------------------------------------------------------

#this is python code to kickstart the program if not imported
if __name__ == '__main__':
    main()
    #profile.run('main()')
